/**
 *  Created on  : 07/06/2012
 *  Author      : Ing. Javier Silva Pérez - [javier]
 *  Description :
 *  	Wraps the information of a Elliptic Curve Public Key and contain some parse functions 
 *  	to interact with JCE and SpongyCastle EC public Key representations
 */
package cinvestav.cryptography.pki.android.key;

import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.jce.spec.ECPublicKeySpec;
import org.spongycastle.math.ec.ECPoint;

import cinvestav.cryptography.pki.android.ec.ECDomainParameters;
import cinvestav.cryptography.pki.android.ec.ECPointF2m;
import cinvestav.cryptography.pki.android.ec.ECPointFp;
import cinvestav.cryptography.pki.android.exception.AndroidCryptoUtilsException;

/**
 * Wraps the information of a Elliptic Curve Public Key and contain some parse
 * functions to interact with JCE and SpongyCastle EC public Key representations
 * 
 * @author Ing. Javier Silva Pérez - [javier]
 * @since 07/06/2012
 * @version 1.0
 */
public class ECPublicKey {

	/**
	 * Parameters for the public key, curve, order, cofactor, etc
	 */
	private ECDomainParameters params;

	/**
	 * Point in the curve that represents the Public key;
	 */
	private ECPoint q;

	/**
	 * Constructor of a EC public key over Fp
	 * 
	 * @param params
	 *            Curve parameters, this should represent a curve over Fp
	 * @param q
	 *            Point over Fp
	 * @throws AndroidCryptoUtilsException
	 *             if the point and the curve are not in the same field (Fp)
	 */
	public ECPublicKey(ECDomainParameters params, ECPointFp q)
			throws AndroidCryptoUtilsException {
		super();
		if (!params.getField().equalsIgnoreCase(ECDomainParameters.FIELD_FP)) {
			throw new AndroidCryptoUtilsException(
					"ECPublicKey constructor error: Incongruent Curve and point fields");
		}
		this.params = params;
		this.q = q;
	}

	/**
	 * Constructor of a EC public key over F2m
	 * 
	 * @param params
	 *            Curve parameters, this should represent a curve over F2m
	 * @param q
	 *            Point over F2m
	 * @throws AndroidCryptoUtilsException
	 *             if the point and the curve are not in the same field (F2m)
	 */
	public ECPublicKey(ECDomainParameters params, ECPointF2m q)
			throws AndroidCryptoUtilsException {
		super();
		if (!params.getField().equalsIgnoreCase(ECDomainParameters.FIELD_F2M)) {
			throw new AndroidCryptoUtilsException(
					"ECPublicKey constructor error: Incongruent Curve and point fields");
		}
		this.params = params;
		this.q = q;
	}

	/**
	 * @return the params
	 */
	public ECDomainParameters getParams() {
		return params;
	}

	/**
	 * @param params
	 *            the params to set
	 */
	public void setParams(ECDomainParameters params) {
		this.params = params;
	}

	/**
	 * @return the q point of the public key
	 * @throws AndroidCryptoUtilsException
	 *             if the point in the publicKey is not a valid point in Fp
	 */
	public ECPointFp getQFp() throws AndroidCryptoUtilsException {
		return ECPointFp.parse(q);
	}

	/**
	 * @return the q point of the public key
	 * @throws AndroidCryptoUtilsException
	 *             if the point in the publicKey is not a valid point in F2m
	 */
	public ECPointF2m getQF2m() throws AndroidCryptoUtilsException {
		return ECPointF2m.parse(q);
	}

	/**
	 * Gets the point Q of the public Key
	 * 
	 * @return Could return a ECPoint2m if the point is over F2m or ECPointFp if
	 *         the point is over Fp
	 * @throws AndroidCryptoUtilsException
	 *             if the point is neither over Fp nor F2m
	 */
	public Object getQ() throws AndroidCryptoUtilsException {
		if (q instanceof ECPointF2m) {
			return ECPointF2m.parse(q);
		} else if (q instanceof ECPointFp) {
			return ECPointFp.parse(q);
		}
		throw new AndroidCryptoUtilsException("Invalid format of the point");
	}

	/**
	 * Set the point Q to be over Fp
	 * 
	 * @param q
	 *            The point of the public key
	 * @throws AndroidCryptoUtilsException
	 *             if the parameters of the public key curve does not correspond
	 *             to a Fp field
	 */
	public void setQ(ECPointFp q) throws AndroidCryptoUtilsException {
		if (!params.getField().equalsIgnoreCase(ECDomainParameters.FIELD_FP)) {
			throw new AndroidCryptoUtilsException(
					"ECPublicKey constructor error: Incongruent Curve and point fields");
		}
		this.q = q;
	}

	/**
	 * Set the point Q to be over F2m
	 * 
	 * @param q
	 *            The point of the public key
	 * @throws AndroidCryptoUtilsException
	 *             if the parameters of the public key curve does not correspond
	 *             to a F2m field
	 */
	public void setQ(ECPointF2m q) throws AndroidCryptoUtilsException {
		if (!params.getField().equalsIgnoreCase(ECDomainParameters.FIELD_F2M)) {
			throw new AndroidCryptoUtilsException(
					"ECPublicKey constructor error: Incongruent Curve and point fields");
		}
		this.q = q;
	}

	/**
	 * Parse the SpongyCastle ECPublicKeyParameters (
	 * {@link org.spongycastle.crypto.params.ECPublicKeyParameters}) object
	 * 
	 * @param publicKey
	 *            SpongyCastle ECPublicKeyParameters object
	 * @return a new ECPublicKey object with the values of the received key
	 * @throws AndroidCryptoUtilsException
	 *             if the public key point and the curve parameters are
	 *             incongruent (has different fields)
	 */
	public static ECPublicKey parse(ECPublicKeyParameters publicKey)
			throws AndroidCryptoUtilsException {
		ECPublicKey publicKeyRes;
		ECDomainParameters param = ECDomainParameters.parse(publicKey
				.getParameters());
		ECPoint qAux = publicKey.getQ();
		if (qAux instanceof ECPoint.F2m
				&& param.getField().equalsIgnoreCase(
						ECDomainParameters.FIELD_F2M)) {
			publicKeyRes = new ECPublicKey(param, ECPointF2m.parse(qAux));
		} else if (qAux instanceof ECPoint.Fp
				&& param.getField().equalsIgnoreCase(
						ECDomainParameters.FIELD_FP)) {
			publicKeyRes = new ECPublicKey(param, ECPointFp.parse(qAux));
		} else {
			throw new AndroidCryptoUtilsException(
					"ECPublicKey constructor error: Incongruent Curve and point fields");
		}
		return publicKeyRes;

	}

	/**
	 * Parse the calling object to SpongyCastle ECPublicKeyParameters type (
	 * {@link org.spongycastle.crypto.params.ECPublicKeyParameters})
	 * 
	 * @return A SC ECPublicKeyParameters with the values of the calling object
	 */
	public ECPublicKeyParameters parseToECPublicKeyParameters() {
		ECPublicKeyParameters publicKey = new ECPublicKeyParameters(this.q,
				this.params.parseToECDomainParameters());
		return publicKey;
	}

	/**
	 * Parse the SpongyCastle ECPublicKeySpec (
	 * {@link org.spongycastle.jce.spec.ECPublicKeySpec;}) object
	 * 
	 * @param publicKey
	 *            SpongyCastle ECPublicKeySpec object
	 * @return a new ECPublicKey object with the values of the received key
	 * @throws AndroidCryptoUtilsException
	 *             if the public key point and the curve parameters are
	 *             incongruent (has different fields)
	 */
	public static ECPublicKey parse(ECPublicKeySpec publicKey)
			throws AndroidCryptoUtilsException {
		ECPublicKey publicKeyRes;
		ECDomainParameters param = ECDomainParameters.parse(publicKey
				.getParams());
		ECPoint qAux = publicKey.getQ();
		if (qAux instanceof ECPoint.F2m
				&& param.getField().equalsIgnoreCase(
						ECDomainParameters.FIELD_F2M)) {
			publicKeyRes = new ECPublicKey(param, ECPointF2m.parse(qAux));
		} else if (qAux instanceof ECPoint.Fp
				&& param.getField().equalsIgnoreCase(
						ECDomainParameters.FIELD_FP)) {
			publicKeyRes = new ECPublicKey(param, ECPointFp.parse(qAux));
		} else {
			throw new AndroidCryptoUtilsException(
					"ECPublicKey constructor error: Incongruent Curve and point fields");
		}
		return publicKeyRes;
	}

	/**
	 * Parse the calling object to SpongyCastle ECPublicKeySpec type (
	 * {@link org.spongycastle.jce.spec.ECPublicKeySpec})
	 * 
	 * @return A SC ECPublicKeySpec with the values of the calling object
	 */
	public ECPublicKeySpec parseToECPublicKeySpec() {
		ECPublicKeySpec publicKey = new ECPublicKeySpec(this.q,
				this.params.parseToECParameterSpec());
		return publicKey;
	}

}
